Otključajte Pythonov 'email' paket. Naučite konstruirati složene MIME poruke i učinkovito i globalno parsirati dolazne e-pošte za izdvajanje podataka.
Ovladavanje Pythonovim 'email' paketom: Umijeće konstruiranja MIME poruka i robusnog parsiranja
E-pošta ostaje kamen temeljac globalne komunikacije, nezaobilazna za osobnu korespondenciju, poslovne operacije i automatizirane sistemske obavijesti. Iza svake e-pošte s obogaćenim tekstom, svakog privitka i svakog pažljivo formatiranog potpisa leži složenost Multipurpose Internet Mail Extensions (MIME). Za programere, posebno one koji rade s Pythonom, ovladavanje programskim konstruiranjem i parsiranjem ovih MIME poruka ključna je vještina.
Pythonov ugrađeni paket email
pruža robustan i sveobuhvatan okvir za rukovanje e-poštom. Nije samo za slanje jednostavnog teksta; dizajniran je da apstrahira zamršene detalje MIME-a, omogućujući vam stvaranje sofisticiranih e-poruka i izdvajanje specifičnih podataka iz dolaznih s izvanrednom preciznošću. Ovaj će vas vodič odvesti u duboko istraživanje dva primarna aspekta ovog paketa: konstruiranje MIME poruka za slanje i njihovo parsiranje za izdvajanje podataka, pružajući globalnu perspektivu najboljih praksi.
Razumijevanje i konstruiranja i parsiranja ključno je. Kada konstruirate poruku, vi u biti definirate njezinu strukturu i sadržaj za tumačenje drugom sustavu. Kada parsirate, vi tumačite strukturu i sadržaj koje je definirao drugi sustav. Duboko razumijevanje jednog uvelike pomaže u ovladavanju drugim, što dovodi do otpornijih i interoperabilnih aplikacija za e-poštu.
Razumijevanje MIME-a: Okosnica moderne e-pošte
Prije nego što zaronite u Python specifičnosti, bitno je shvatiti što je MIME i zašto je toliko vitalan. Izvorno, poruke e-pošte bile su ograničene na običan tekst (7-bitni ASCII znakovi). MIME, uveden početkom 1990-ih, proširio je mogućnosti e-pošte za podršku:
- Ne-ASCII znakovi: Omogućuju tekst na jezicima poput arapskog, kineskog, ruskog ili bilo kojem drugom jeziku koji koristi znakove izvan ASCII skupa.
- Privitci: Slanje datoteka poput dokumenata, slika, zvuka i videa.
- Formatiranje obogaćenog teksta: HTML e-pošta s podebljavanjem, kurzivom, bojama i rasporedom.
- Više dijelova: Kombiniranje običnog teksta, HTML-a i privitaka unutar jedne e-pošte.
MIME to postiže dodavanjem specifičnih zaglavlja poruci e-pošte i strukturiranjem njenog tijela u različite "dijelove". Ključna MIME zaglavlja na koja ćete naići uključuju:
Content-Type:
Određuje vrstu podataka u dijelu (npr.text/plain
,text/html
,image/jpeg
,application/pdf
,multipart/alternative
). Često uključuje i parametarcharset
(npr.charset=utf-8
).Content-Transfer-Encoding:
Označava kako bi klijent e-pošte trebao dekodirati sadržaj (npr.base64
za binarne podatke,quoted-printable
za uglavnom tekst s nekim ne-ASCII znakovima).Content-Disposition:
Predlaže kako bi klijent e-pošte primatelja trebao prikazati dio (npr.inline
za prikaz unutar tijela poruke,attachment
za datoteku koju treba spremiti).
Pythonov paket email
: Detaljan pregled
Pythonov paket email
sveobuhvatna je biblioteka dizajnirana za programsko stvaranje, parsiranje i modificiranje poruka e-pošte. Izgrađen je oko koncepta Message
objekata, koji predstavljaju strukturu e-pošte.
Ključni moduli unutar paketa uključuju:
email.message:
Sadrži osnovnu klasuEmailMessage
, koja je primarno sučelje za stvaranje i manipulaciju porukama e-pošte. To je vrlo fleksibilna klasa koja automatski obrađuje MIME detalje.email.mime:
Pruža naslijeđene klase (poputMIMEText
,MIMEMultipart
) koje nude eksplicitniju kontrolu nad MIME strukturom. Iako jeEmailMessage
općenito preferiran za novi kod zbog svoje jednostavnosti, razumijevanje ovih klasa može biti korisno.email.parser:
Nudi klase poputBytesParser
iParser
za pretvaranje sirovih podataka e-pošte (bajtova ili stringova) uEmailMessage
objekte.email.policy:
Definira pravila koja kontroliraju kako se poruke e-pošte konstruiraju i parsiraju, utječući na kodiranje zaglavlja, završetke redaka i rukovanje pogreškama.
Za većinu modernih slučajeva upotrebe, prvenstveno ćete komunicirati s klasom email.message.EmailMessage
za konstruiranje i za objekt parsirane poruke. Njezine metode uvelike pojednostavljuju ono što je nekad bio opsežniji proces s naslijeđenim email.mime
klasama.
Konstruiranje MIME poruka: Izgradnja e-pošte s preciznošću
Konstruiranje e-pošte uključuje sastavljanje različitih komponenti (teksta, HTML-a, privitaka) u valjanu MIME strukturu. Klasa EmailMessage
značajno pojednostavljuje ovaj proces.
Osnovne tekstualne e-pošte
Najjednostavnija e-pošta je običan tekst. Možete je stvoriti i postaviti osnovna zaglavlja bez napora:
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Pozdrav iz Pythona'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Pozdrav, ovo je jednostavna tekstualna e-pošta poslana iz Pythona.\n\nSrdačan pozdrav,\nVaša Python Skripta')
print(msg.as_string())
Objašnjenje:
EmailMessage()
stvara prazan objekt poruke.- Pristup poput rječnika (
msg['Subject'] = ...
) postavlja uobičajena zaglavlja. set_content()
dodaje primarni sadržaj e-pošte. Prema zadanim postavkama, pretpostavljaContent-Type: text/plain; charset="utf-8"
.as_string()
serijalizira poruku u string format pogodan za slanje putem SMTP-a ili spremanje u datoteku.
Dodavanje HTML sadržaja
Za slanje HTML e-pošte, jednostavno navedite vrstu sadržaja prilikom pozivanja set_content()
. Dobra je praksa osigurati alternativu u običnom tekstu za primatelje čiji klijenti e-pošte ne prikazuju HTML, ili iz razloga pristupačnosti.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Vaš HTML bilten'
msg['From'] = 'newsletter@example.com'
msg['To'] = 'subscriber@example.com'
html_content = """
<html>
<head></head>
<body>
<h1>Dobrodošli u naše globalno ažuriranje!</h1>
<p>Poštovani pretplatniče,</p>
<p>Ovo je vaše <strong>najnovije ažuriranje</strong> iz cijelog svijeta.</p>
<p>Posjetite našu <a href="http://www.example.com">web stranicu</a> za više.</p>
<p>Srdačan pozdrav,<br>Tim</p>
</body>
</html>
"""
# Dodajte HTML verziju
msg.add_alternative(html_content, subtype='html')
# Dodajte zamjenski običan tekst
plain_text_content = (
"Dobrodošli u naše globalno ažuriranje!\n\n"
"Poštovani pretplatniče,\n\n"
"Ovo je vaše najnovije ažuriranje iz cijelog svijeta.\n"
"Posjetite našu web stranicu za više: http://www.example.com\n\n"
"Srdačan pozdrav,\nTim"
)
msg.add_alternative(plain_text_content, subtype='plain')
print(msg.as_string())
Objašnjenje:
add_alternative()
se koristi za dodavanje različitih prikaza *istog* sadržaja. Klijent e-pošte prikazat će "najbolji" koji može podržati (obično HTML).- Ovo automatski stvara
multipart/alternative
MIME strukturu.
Rukovanje privicima
Prilaganje datoteka jednostavno je pomoću add_attachment()
. Možete priložiti bilo koju vrstu datoteke, a paket obrađuje odgovarajuće MIME tipove i kodiranja (obično base64
).
from email.message import EmailMessage
from pathlib import Path
# Stvorite fiktivne datoteke za demonstraciju
Path('report.pdf').write_bytes(b'%PDF-1.4\n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj\n2 0 obj<</Count 0>>endobj\nxref\n0 3\n0000000000 65535 f\n0000000009 00000 n\n0000000052 00000 n\ntrailer<</Size 3/Root 1 0 R>>startxref\n104\n%%EOF') # Vrlo jednostavan, nevažeći PDF placeholder
Path('logo.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82') # 1x1 prozirni PNG placeholder
msg = EmailMessage()
msg['Subject'] = 'Važan dokument i slika'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
msg.set_content('Molimo pronađite priloženi izvještaj i logotip tvrtke.')
# Priložite PDF datoteku
with open('report.pdf', 'rb') as f:
file_data = f.read()
msg.add_attachment(
file_data,
maintype='application',
subtype='pdf',
filename='Annual_Report_2024.pdf'
)
# Priložite datoteku slike
with open('logo.png', 'rb') as f:
image_data = f.read()
msg.add_attachment(
image_data,
maintype='image',
subtype='png',
filename='CompanyLogo.png'
)
print(msg.as_string())
# Očistite fiktivne datoteke
Path('report.pdf').unlink()
Path('logo.png').unlink()
Objašnjenje:
add_attachment()
uzima sirove bajtove sadržaja datoteke.maintype
isubtype
određuju MIME tip (npr.application/pdf
,image/png
). To je ključno kako bi klijent e-pošte primatelja ispravno identificirao i obradio privitak.filename
pruža naziv pod kojim će primatelj spremiti privitak.- Ovo automatski postavlja
multipart/mixed
strukturu.
Stvaranje višedijelnih poruka
Kada imate poruku s HTML tijelom, zamjenskim običnim tekstom i ugrađenim slikama ili povezanim datotekama, potrebna vam je složenija višedijelna struktura. Klasa EmailMessage
to inteligentno obrađuje pomoću add_related()
i add_alternative()
.
Uobičajen scenarij je HTML e-pošta sa slikom ugrađenom izravno u HTML (takozvana "inline" slika). Ovo koristi multipart/related
.
from email.message import EmailMessage
from pathlib import Path
# Stvorite fiktivnu slikovnu datoteku za demonstraciju (1x1 prozirni PNG)
Path('banner.png').write_bytes(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\x0cIDAT\x08\x99c`\x00\x00\x00\x02\x00\x01\xe2!\x00\xa0\x00\x00\x00\x00IEND\xaeB`\x82')
msg = EmailMessage()
msg['Subject'] = 'Primjer ugrađene slike'
msg['From'] = 'sender@example.com'
msg['To'] = 'recipient@example.com'
# Verzija običnog teksta (rezervna)
plain_text = 'Pogledajte naš nevjerojatan banner!\n\n[Slika: Banner.png]\n\nPosjetite našu stranicu.'
msg.set_content(plain_text, subtype='plain') # Postavite početni sadržaj običnog teksta
# HTML verzija (s CID-om za ugrađenu sliku)
html_content = """
<html>
<head></head>
<body>
<h1>Naša najnovija ponuda!</h1>
<p>Poštovani kupče,</p>
<p>Ne propustite našu posebnu globalnu promociju:</p>
<img src="cid:my-banner-image" alt="Promotion Banner">
<p>Kliknite <a href="http://www.example.com">ovdje</a> kako biste saznali više.</p>
</body>
</html>
"""
msg.add_alternative(html_content, subtype='html') # Dodajte HTML alternativu
# Dodajte ugrađenu sliku (povezani sadržaj)
with open('banner.png', 'rb') as img_file:
image_data = img_file.read()
msg.add_related(
image_data,
maintype='image',
subtype='png',
cid='my-banner-image' # Ovaj CID odgovara 'src' u HTML-u
)
print(msg.as_string())
# Očistite fiktivnu datoteku
Path('banner.png').unlink()
Objašnjenje:
set_content()
uspostavlja početni sadržaj (ovdje, običan tekst).add_alternative()
dodaje HTML verziju, stvarajućimultipart/alternative
strukturu koja sadrži dijelove običnog teksta i HTML-a.add_related()
se koristi za sadržaj koji je "povezan" s jednim od dijelova poruke, tipično ugrađene slike u HTML-u. Uzima parametarcid
(Content-ID), na koji se zatim referencira u HTML tagu<img src="cid:my-banner-image">
.- Konačna struktura bit će
multipart/mixed
(ako su postojali vanjski privici) koja sadržimultipart/alternative
dio, koji pak sadržimultipart/related
dio.multipart/related
dio sadrži HTML i ugrađenu sliku. KlasaEmailMessage
rukuje ovom složenošću ugniježđenja za vas.
Kodiranje i skupovi znakova za globalni doseg
Za međunarodnu komunikaciju, pravilno kodiranje znakova je najvažnije. Paket email
, prema zadanim postavkama, snažno podržava korištenje UTF-8, što je univerzalni standard za rukovanje raznolikim skupovima znakova iz cijelog svijeta.
from email.message import EmailMessage
msg = EmailMessage()
msg['Subject'] = 'Globalni znakovi: こんにちは, Привет, नमस्ते'
msg['From'] = 'global_sender@example.com'
msg['To'] = 'global_recipient@example.com'
# Japanski, ruski i hindi znakovi
content = "Ova poruka sadrži raznolike globalne znakove:\n"
content += "こんにちは (Japanski)\n"
content += "Привет (Ruski)\n"
content += "नमस्ते (Hindi)\n\n"
content += "Paket 'email' graciozno rukuje UTF-8 kodiranjem."
msg.set_content(content)
print(msg.as_string())
Objašnjenje:
- Kada
set_content()
primi Python string, automatski ga kodira u UTF-8 i postavlja zaglavljeContent-Type: text/plain; charset="utf-8"
. - Ako sadržaj to zahtijeva (npr. sadrži mnogo ne-ASCII znakova), može primijeniti i
Content-Transfer-Encoding: quoted-printable
ilibase64
kako bi se osigurao siguran prijenos preko starijih sustava e-pošte. Paket to automatski obrađuje prema odabranom pravilu.
Prilagođena zaglavlja i pravila
Možete dodati bilo koje prilagođeno zaglavlje e-pošti. Pravila (iz email.policy
) definiraju kako se poruke obrađuju, utječući na aspekte poput kodiranja zaglavlja, završetaka redaka i rukovanja pogreškama. Zadano pravilo je općenito dobro, ali možete odabrati SMTP
za strogu usklađenost sa SMTP-om ili definirati vlastita.
from email.message import EmailMessage
from email import policy
msg = EmailMessage(policy=policy.SMTP)
msg['Subject'] = 'E-pošta s prilagođenim zaglavljem'
msg['From'] = 'info@example.org'
msg['To'] = 'user@example.org'
msg['X-Custom-Header'] = 'Ovo je prilagođena vrijednost za praćenje'
msg['Reply-To'] = 'support@example.org'
msg.set_content('Ova e-pošta demonstrira prilagođena zaglavlja i pravila.')
print(msg.as_string())
Objašnjenje:
- Korištenje
policy=policy.SMTP
osigurava strogu usklađenost sa SMTP standardima, što može biti ključno za isporučivost. - Prilagođena zaglavlja dodaju se baš kao i standardna. Često počinju s
X-
kako bi označili nestandardna zaglavlja.
Parsiranje MIME poruka: Izdvajanje informacija iz dolaznih e-pošte
Parsiranje uključuje uzimanje sirovih podataka e-pošte (obično primljenih putem IMAP-a ili iz datoteke) i pretvaranje u objekt EmailMessage
koji zatim možete jednostavno pregledati i manipulirati.
Učitavanje i početno parsiranje
E-poštu ćete obično primati kao sirove bajtove. Za to se koristi email.parser.BytesParser
(ili pomoćne funkcije email.message_from_bytes()
).
from email.parser import BytesParser
from email.policy import default
raw_email_bytes = b"""
From: sender@example.com
To: recipient@example.com
Subject: Test E-pošta s osnovnim zaglavljima
Date: Mon, 1 Jan 2024 10:00:00 +0000
Content-Type: text/plain; charset="utf-8"
Ovo je tijelo e-pošte.
To je jednostavan test.
"""
# Korištenje BytesParser-a
parser = BytesParser(policy=default)
msg = parser.parsebytes(raw_email_bytes)
# Ili korištenje pomoćne funkcije
# from email import message_from_bytes
# msg = message_from_bytes(raw_email_bytes, policy=default)
print(f"Predmet: {msg['subject']}")
print(f"Od: {msg['from']}")
print(f"Vrsta sadržaja: {msg['Content-Type']}")
Objašnjenje:
BytesParser
uzima sirove bajt podatke (tako se prenose e-pošte) i vraćaEmailMessage
objekt.policy=default
specificira pravila parsiranja.
Pristup zaglavljima
Zaglavlja su lako dostupna putem ključeva sličnih rječniku. Paket automatski obrađuje dekodiranje kodiranih zaglavlja (npr. predmete s međunarodnim znakovima).
# ... (koristeći 'msg' objekt iz prethodnog primjera parsiranja)
print(f"Datum: {msg['date']}")
print(f"ID poruke: {msg['Message-ID'] if 'Message-ID' in msg else 'N/A'}")
# Rukovanje višestrukim zaglavljima (npr. zaglavlja 'Received')
# from email.message import EmailMessage # Ako još nije uvezeno
# from email import message_from_string # Za brzi primjer stringa
multi_header_email = message_from_string(
"""
From: a@example.com
To: b@example.com
Subject: Test s višestrukim zaglavljima
Received: from client.example.com (client.example.com [192.168.1.100])
by server.example.com (Postfix) with ESMTP id 123456789
for <b@example.com>; Mon, 1 Jan 2024 10:00:00 +0000 (GMT)
Received: from mx.another.com (mx.another.com [192.168.1.101])
by server.example.com (Postfix) with ESMTP id 987654321
for <b@example.com>; Mon, 1 Jan 2024 09:59:00 +0000 (GMT)
Ovdje je sadržaj tijela.
"""
)
received_headers = multi_header_email.get_all('received')
if received_headers:
print("\nPrimljena zaglavlja:")
for header in received_headers:
print(f"- {header}")
Objašnjenje:
- Pristup zaglavlju vraća njegovu vrijednost kao string.
get_all('header-name')
je koristan za zaglavlja koja se mogu pojaviti više puta (poputReceived
).- Paket obrađuje dekodiranje zaglavlja, tako da se vrijednosti poput
Subject: =?utf-8?Q?Global_Characters:_=E3=81=93=E3=82=93=E3=81=AB=E3=81=A1=E3=81=AF?=
automatski pretvaraju u čitljive stringove.
Izdvajanje sadržaja tijela
Izdvajanje stvarnog tijela poruke zahtijeva provjeru je li poruka višedijelna. Za višedijelne poruke, iterirate kroz njezine dijelove.
from email.message import EmailMessage
from email import message_from_string
multipart_email_raw = """
From: multi@example.com
To: user@example.com
Subject: Test višedijelna e-pošta
Content-Type: multipart/alternative; boundary="_----------=_12345"
--_----------=_12345
Content-Type: text/plain; charset="utf-8"
Pozdrav iz dijela običnog teksta!
--_----------=_12345
Content-Type: text/html; charset="utf-8"
<html>
<body>
<h1>Pozdrav iz HTML dijela!</h1>
<p>Ovo je <strong>obogaćena tekstualna</strong> e-pošta.</p>
</body>
</html>
--_----------=_12345--
"""
msg = message_from_string(multipart_email_raw)
if msg.is_multipart():
print("\n--- Tijelo višedijelne e-pošte ---")
for part in msg.iter_parts():
content_type = part.get_content_type()
charset = part.get_content_charset() or 'utf-8' # Zadani utf-8 ako nije specificiran
payload = part.get_payload(decode=True) # Dekodirajte bajtove korisnog tereta
try:
decoded_content = payload.decode(charset)
print(f"Vrsta sadržaja: {content_type}, Skup znakova: {charset}\nSadržaj:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Vrsta sadržaja: {content_type}, Skup znakova: {charset}\nSadržaj: (Binarni ili nedekodabilni podaci)\n")
# Rukovanje binarnim podacima, ili pokušaj zamjenskog kodiranja
else:
print("\n--- Tijelo jednodijelne e-pošte ---")
charset = msg.get_content_charset() or 'utf-8'
payload = msg.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
print(f"Vrsta sadržaja: {msg.get_content_type()}, Skup znakova: {charset}\nSadržaj:\n{decoded_content}\n")
except UnicodeDecodeError:
print(f"Sadržaj: (Binarni ili nedekodabilni podaci)\n")
Objašnjenje:
is_multipart()
određuje ima li e-pošta više dijelova.iter_parts()
iterira kroz sve pod-dijelove višedijelne poruke.get_content_type()
vraća puni MIME tip (npr.text/plain
).get_content_charset()
izvlači skup znakova iz zaglavljaContent-Type
.get_payload(decode=True)
je ključno: vraća *dekodirani* sadržaj kao bajtove. Zatim te bajtove trebate.decode()
koristeći ispravan skup znakova kako biste dobili Python string.
Rukovanje privicima tijekom parsiranja
Privitci su također dijelovi višedijelne poruke. Možete ih identificirati pomoću zaglavlja Content-Disposition
i spremiti njihov dekodirani sadržaj.
from email.message import EmailMessage
from email import message_from_string
import os
# Primjer e-pošte s jednostavnim privitkom
email_with_attachment = """
From: attach@example.com
To: user@example.com
Subject: Dokument priložen
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="_----------=_XYZ"
--_----------=_XYZ
Content-Type: text/plain; charset="utf-8"
Ovdje je vaš traženi dokument.
--_----------=_XYZ
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="document.pdf"
JVBERi0xLjQKMSAwIG9iagpbL1BERi9UZXh0L0ltYWdlQy9JbWFnZUkvSW1hZ0VCXQplbmRvYmoK
--_----------=_XYZ--
"""
msg = message_from_string(email_with_attachment)
output_dir = 'parsed_attachments'
os.makedirs(output_dir, exist_ok=True)
print("\n--- Obrada privitaka ---")
for part in msg.iter_attachments():
filename = part.get_filename()
if filename:
filepath = os.path.join(output_dir, filename)
try:
with open(filepath, 'wb') as f:
f.write(part.get_payload(decode=True))
print(f"Spremljen privitak: {filepath} (Vrsta: {part.get_content_type()})")
except Exception as e:
print(f"Greška pri spremanju {filename}: {e}")
else:
print(f"Pronađen privitak bez naziva datoteke (Vrsta sadržaja: {part.get_content_type()})")
# Očistite izlazni direktorij
# import shutil
# shutil.rmtree(output_dir)
Objašnjenje:
iter_attachments()
posebno vraća dijelove koji su vjerojatno privitci (tj. imaju zaglavljeContent-Disposition: attachment
ili nisu drugačije klasificirani).get_filename()
izvlači naziv datoteke iz zaglavljaContent-Disposition
.part.get_payload(decode=True)
dohvaća sirovi binarni sadržaj privitka, već dekodiran izbase64
iliquoted-printable
.
Dekodiranje kodiranja i skupova znakova
Paket email
izvrsno automatski dekodira uobičajena prijenosna kodiranja (poput base64
, quoted-printable
) kada pozovete get_payload(decode=True)
. Za sam tekstualni sadržaj, pokušava koristiti charset
specificiran u zaglavlju Content-Type
. Ako skup znakova nije specificiran ili je nevažeći, možda ćete ga morati graciozno obraditi.
from email.message import EmailMessage
from email import message_from_string
# Primjer s potencijalno problematičnim skupom znakova
email_latin1 = """
From: legacy@example.com
To: new_system@example.com
Subject: Posebni znakovi: àéíóú
Content-Type: text/plain; charset="iso-8859-1"
Ova poruka sadrži Latin-1 znakove: àéíóú
"""
msg = message_from_string(email_latin1)
if msg.is_multipart():
for part in msg.iter_parts():
payload = part.get_payload(decode=True)
charset = part.get_content_charset() or 'utf-8'
try:
print(f"Dekodirano (Skup znakova: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Dekodiranje s {charset} nije uspjelo. Pokušavam rezervno...")
# Rezervni skup znakova ili 'latin-1' ako ga očekujete
print(f"Dekodirano (Rezervni Latin-1): {payload.decode('latin-1', errors='replace')}")
else:
payload = msg.get_payload(decode=True)
charset = msg.get_content_charset() or 'utf-8'
try:
print(f"Dekodirano (Skup znakova: {charset}): {payload.decode(charset)}")
except UnicodeDecodeError:
print(f"Dekodiranje s {charset} nije uspjelo. Pokušavam rezervno...")
print(f"Dekodirano (Rezervni Latin-1): {payload.decode('latin-1', errors='replace')}")
Objašnjenje:
- Uvijek pokušajte koristiti skup znakova naveden u zaglavlju
Content-Type
. - Koristite
try-except UnicodeDecodeError
blok za robusnost, posebno kada radite s e-poštom iz različitih i potencijalno nestandardnih izvora. errors='replace'
ilierrors='ignore'
mogu se koristiti s.decode()
za rukovanje znakovima koji se ne mogu mapirati na ciljano kodiranje, sprječavajući rušenja.
Napredni scenariji parsiranja
E-pošta u stvarnom svijetu može biti vrlo složena, s ugniježđenim višedijelnim strukturama. Rekurzivna priroda paketa email
čini navigaciju kroz njih jednostavnom. Možete kombinirati is_multipart()
s iter_parts()
za prolazak kroz duboko ugniježđene poruke.
from email.message import EmailMessage
from email import message_from_string
def parse_email_part(part, indent=0):
prefix = " " * indent
content_type = part.get_content_type()
charset = part.get_content_charset() or 'N/A'
print(f"{prefix}Vrsta dijela: {content_type}, Skup znakova: {charset}")
if part.is_multipart():
for subpart in part.iter_parts():
parse_email_part(subpart, indent + 1)
elif part.get_filename(): # To je privitak
print(f"{prefix} Privitak: {part.get_filename()} (Veličina: {len(part.get_payload(decode=True))} bajtova)")
else: # To je običan dio teksta/html tijela
payload = part.get_payload(decode=True)
try:
decoded_content = payload.decode(charset)
# print(f"{prefix} Sadržaj (prvih 100 znakova): {decoded_content[:100]}...") # Radi sažetosti
except UnicodeDecodeError:
print(f"{prefix} Sadržaj: (Binarni ili nedekodabilni tekst)")
complex_email_raw = """
From: complex@example.com
To: receiver@example.com
Subject: Složena e-pošta s HTML-om, običnim tekstom i privitkom
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="outer_boundary"
--outer_boundary
Content-Type: multipart/alternative; boundary="inner_boundary"
--inner_boundary
Content-Type: text/plain; charset="utf-8"
Sadržaj običnog teksta.
--inner_boundary
Content-Type: text/html; charset="utf-8"
<html><body><h2>HTML Sadržaj</h2></body></html>
--inner_boundary--
--outer_boundary
Content-Type: application/octet-stream
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="data.bin"
SGVsbG8gV29ybGQh
--outer_boundary--
"""
msg = message_from_string(complex_email_raw)
print("\n--- Prolazak kroz složenu strukturu e-pošte ---")
parse_email_part(msg)
Objašnjenje:
- Rekurzivna funkcija
parse_email_part
demonstrira kako proći kroz cijelo stablo poruke, identificirajući višedijelne dijelove, privitke i sadržaj tijela na svakoj razini. - Ovaj je obrazac vrlo fleksibilan za izdvajanje specifičnih vrsta sadržaja iz duboko ugniježđenih e-pošte.
Konstruiranje vs. Parsiranje: Usporedna perspektiva
Iako su to različite operacije, konstruiranje i parsiranje su dvije strane iste medalje: rukovanje MIME porukama. Razumijevanje jednog neizbježno pomaže u razumijevanju drugog.
Konstruiranje (Slanje):
- Fokus: Ispravno sastavljanje zaglavlja, sadržaja i privitaka u MIME strukturu usklađenu sa standardima.
- Glavni alat:
email.message.EmailMessage
s metodama kao što suset_content()
,add_attachment()
,add_alternative()
,add_related()
. - Ključni izazovi: Osiguravanje ispravnih MIME tipova, skupova znakova (posebno UTF-8 za globalnu podršku),
Content-Transfer-Encoding
i pravilnog formatiranja zaglavlja. Pogreške mogu dovesti do toga da se e-pošta ne prikazuje ispravno, da se privitci oštete ili da se poruke označe kao spam.
Parsiranje (Primanje):
- Fokus: Rastavljanje sirovog bajt stream-a e-pošte na njegove sastavne dijelove, izdvajanje specifičnih zaglavlja, sadržaja tijela i privitaka.
- Glavni alat:
email.parser.BytesParser
iliemail.message_from_bytes()
, zatim navigacija rezultirajućim objektomEmailMessage
s metodama kao što suis_multipart()
,iter_parts()
,get_payload()
,get_filename()
i pristup zaglavljima. - Ključni izazovi: Rukovanje loše formiranim e-poštom, ispravno identificiranje kodiranja znakova (posebno kada su dvosmisleni), rješavanje nedostajućih zaglavlja i robusno izdvajanje podataka iz raznolikih MIME struktura.
Poruka koju konstruirate pomoću EmailMessage
trebala bi biti savršeno parsabilna pomoću BytesParser
. Slično tome, razumijevanje MIME strukture proizvedene tijekom parsiranja daje vam uvid u to kako sami izgraditi složene poruke.
Najbolje prakse za globalno rukovanje e-poštom s Pythonom
Za aplikacije koje komuniciraju s globalnom publikom ili obrađuju različite izvore e-pošte, razmotrite ove najbolje prakse:
- Standardizirajte na UTF-8: Uvijek koristite UTF-8 za sav tekstualni sadržaj, i prilikom konstruiranja i prilikom očekivanja tijekom parsiranja. To je globalni standard za kodiranje znakova i izbjegava mojibake (iskrivljeni tekst).
- Provjerite valjanost adresa e-pošte: Prije slanja provjerite valjanost adresa e-pošte primatelja kako biste osigurali isporučivost. Tijekom parsiranja, budite spremni na potencijalno nevažeće ili loše formirane adrese u zaglavljima
From
,To
iliCc
. - Detaljno testirajte: Testirajte konstruiranje e-pošte s raznim klijentima e-pošte (Gmail, Outlook, Apple Mail, Thunderbird) i platformama kako biste osigurali dosljedno prikazivanje HTML-a i privitaka. Za parsiranje, testirajte sa širokim rasponom uzoraka e-pošte, uključujući one s neobičnim kodiranjima, nedostajućim zaglavljima ili složenim ugniježđenim strukturama.
- Sanirajte parsirani unos: Sadržaj izvučen iz dolazne e-pošte uvijek tretirajte kao nepouzdan. Sanirajte HTML sadržaj kako biste spriječili XSS napade ako ga prikazujete u web aplikaciji. Provjerite valjanost naziva datoteka i vrsta privitaka kako biste spriječili putnu putanju ili druge sigurnosne ranjivosti prilikom spremanja datoteka.
- Robusno rukovanje pogreškama: Implementirajte sveobuhvatne
try-except
blokove prilikom dekodiranja korisnog tereta ili pristupa potencijalno nedostajućim zaglavljima. Graciozno rukujte sUnicodeDecodeError
iliKeyError
. - Rukovanje velikim privicima: Budite svjesni veličina privitaka, i prilikom konstruiranja (kako biste izbjegli prekoračenje ograničenja poslužitelja e-pošte) i parsiranja (kako biste spriječili prekomjernu upotrebu memorije ili potrošnju prostora na disku). Razmislite o streamingu velikih privitaka ako to podržava vaš sustav.
- Koristite
email.policy
: Za kritične aplikacije, eksplicitno odaberiteemail.policy
(npr.policy.SMTP
) kako biste osigurali strogu usklađenost sa standardima e-pošte, što može utjecati na isporučivost i interoperabilnost. - Očuvanje metapodataka: Prilikom parsiranja, odlučite koje metapodatke (zaglavlja, izvorni nizovi granica) je važno očuvati, posebno ako gradite sustav za arhiviranje ili prosljeđivanje pošte.
Zaključak
Pythonov paket email
nevjerojatno je moćna i fleksibilna biblioteka za svakoga tko treba programski komunicirati s e-poštom. Ovladavanjem i konstruiranjem MIME poruka i robusnim parsiranjem dolaznih e-pošte, otključavate mogućnost stvaranja sofisticiranih sustava za automatizaciju e-pošte, izgradnju klijenata e-pošte, analizu podataka e-pošte i integraciju funkcionalnosti e-pošte u gotovo svaku aplikaciju.
Paket promišljeno rukuje temeljnim složenostima MIME-a, omogućujući programerima da se usredotoče na sadržaj i logiku svojih interakcija s e-poštom. Bilo da šaljete personalizirane biltene globalnoj publici ili izvlačite kritične podatke iz automatiziranih izvješća sustava, duboko razumijevanje paketa email
pokazat će se neprocjenjivim u izgradnji pouzdanih, interoperabilnih i globalno svjesnih rješenja za e-poštu.